package org.example.mq.client;

import org.example.mq.common.*;
import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Connection {
    private Socket socket = null;
    //因为需要管理多个Channel，所以使用hash表来组织channel
    private ConcurrentHashMap<String,Channel> channelMap = new ConcurrentHashMap<>();

    private InputStream inputStream;
    private OutputStream outputStream;
    private DataInputStream  dataInputStream;
    private DataOutputStream dataOutputStream;

    private ExecutorService callbackPool = null;

    public Connection(String host,int port) throws IOException {
        socket = new Socket(host, port);
        inputStream = socket.getInputStream();
        outputStream = socket.getOutputStream();
        dataInputStream = new DataInputStream(inputStream);
        dataOutputStream = new DataOutputStream(outputStream);

        callbackPool = Executors.newFixedThreadPool(4);

        //创建一个扫描线程，由这个线程不停从socket中读取响应数据，再将数据交给对应的channel负责处理。
        Thread t = new Thread(()->{
            try{
                while(!socket.isClosed()){
                    Response response = readResponse();
                    //System.out.println("[Connection]运行到此");
                    dispatchResponse(response);
                }
            }catch (SocketException e){
                //这个异常表示连接正常断开
                System.out.println("[Connection] 连接正常断开！");
            }catch (IOException | ClassNotFoundException | MqException e){
                System.out.println("[Connection] 连接异常断开！");
                e.printStackTrace();
            }
        });
        t.start();
    }

    //关闭Connection
    public void close(){
        try{
            callbackPool.shutdownNow();
            channelMap.clear();
            inputStream.close();
            outputStream.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //使用这个方法来分别处理当前响应是一个针对控制请求的响应还是服务器推送的消息
    private void dispatchResponse(Response response) throws IOException, ClassNotFoundException, MqException {
        if(response.getType()==0xc){
            //服务器推送的消息数据
            SubScribeReturnArguments subScribeReturnArguments = (SubScribeReturnArguments) BinaryTool.toObject(response.getPayload());
            //根据channelId找到对应的channel
            Channel channel = channelMap.get(subScribeReturnArguments.getChannelId());
            if(channel==null){
                throw new MqException("[Connection] 该消息对应的channel客户端不存在！channelId="+channel.getChannelId());
            }
            //执行channel内部对应的回调函数
            callbackPool.submit(()->{
                try{
                    channel.getConsumer().handleDelivery(subScribeReturnArguments.getConsumeTag(),
                            subScribeReturnArguments.getBasicProperties(), subScribeReturnArguments.getPayload());
                }catch(MqException | IOException e){
                    e.printStackTrace();
                }
            });
        }else{
            //下面是对其他请求的响应
            BasicReturnArguments basicReturnArguments = (BasicReturnArguments) BinaryTool.toObject(response.getPayload());
            //吧结果放到对应的channel的hash表中
            Channel channel = channelMap.get(basicReturnArguments.getChannelId());
            if(channel == null){
                throw new MqException("[Connection] 该消息对应的channel客户端不存在！channelId="+channel.getChannelId());
            }
            channel.putReturns(basicReturnArguments);
        }
    }

    //读取响应
    private Response readResponse() throws IOException {
        Response response = new Response();
        response.setType(dataInputStream.readInt());
        response.setLength(dataInputStream.readInt());
        byte[] payload = new byte[response.getLength()];
        int n = dataInputStream.read(payload);
        if(n!=response.getLength()){
            throw new IOException("[Connection] 读取的响应数据不完整！");
        }
        response.setPayload(payload);
        System.out.println("[Connection] 收到响应！Type="+response.getType()+",Length="+response.getLength());
        return response;
    }

    //发送请求
    public void writeRequest(Request request) throws IOException {
        dataOutputStream.writeInt(request.getType());
        dataOutputStream.writeInt(request.getLength());
        dataOutputStream.write(request.getPayload());

        dataOutputStream.flush();
        System.out.println("[Connection] 发送请求成功！type="+request.getType()+"，length="+request.getLength());
    }

    //在Connection中创建一个Channel
    public Channel createChannel() throws IOException {
        String channelId = "C-" + UUID.randomUUID();
        Channel channel = new Channel(channelId,this);
        //把这个channel放到Connection管理的channel的哈希表中
        channelMap.put(channelId,channel);
        //然后将创建channel告诉给服务器
        boolean ok = channel.createChannel();
        if(!ok){
            //不仅创建失败
            //还要将添加入hash表的channel给删了
            channelMap.remove(channelId);
            return null;
        }
        return channel;
    }
}
